home *** CD-ROM | disk | FTP | other *** search
/ Technotools / Technotools (Chestnut CD-ROM)(1993).ISO / lang_asm / nnansi3 / nnansi.asm < prev    next >
Encoding:
Assembly Source File  |  1989-12-12  |  39.5 KB  |  1,694 lines

  1.     page    60, 132
  2. ;--- nnansi.asm ----------------------------------------------------------
  3. ; New, New ANSI terminal driver.
  4. ; Optimized for speed in the case of multi-character write requests.
  5. ; (C) 1986 Daniel Kegel, Pasadena, CA
  6. ; May be distributed for educational and personal use only
  7. ; The following files make up the driver:
  8. ;    nnansi.asm   - all DOS function handlers except init
  9. ;       nnansi_d.asm - Compilation options
  10. ;    nnansi_p.asm - parameter parser for ANSI escape sequences
  11. ;    nnansi_f.asm - ANSI command handlers
  12. ;    nnansi_i.asm - init DOS function handler
  13. ;
  14. ; Daniel Kegel, Bellevue, Washington & Pasadena, California
  15. ; Revision history:
  16. ; 5  july 85: brought up non-ANSI portion except forgot backspace
  17. ; 6  july 85: split off ANSI stuff into other files, added backspace
  18. ; 11 july 85: fixed horrible bug in getchar; changed dosfns to subroutines
  19. ; 12 july 85: fixed some scrolling bugs, began adding compaq flag
  20. ; 9  aug 85:  added cursor position reporting
  21. ; 10 aug 85:  added output character translation
  22. ; 11 aug 85:  added keyboard redefinition, some EGA 80x43 support
  23. ; 10 sept 85: Tandy 2000 support via compaq flag (finding refresh buffer)
  24. ; 30 Jan 86:  removed Tandy 2000 stuff, added graphics mode support
  25. ; 12 feb 86:  added int 29h handler, added PUSHA/POPA, added direct beep,
  26. ;          direct cursor positioning, takeover of BIOS write_tty,
  27. ;          noticed & squashed 2 related bugs in tab expansion
  28. ; 13 feb 86:  Squashed them again, harder
  29. ; 24 feb 86:  There is a bug in the timing code used by the BEEP routine.
  30. ;          If the addition of the beep period to the
  31. ;          BIOS low timer word results in an overflow, the beep will be
  32. ;          supressed. Also made code compatible eith earlier versions
  33. ;          of assembler.
  34.  
  35. ; Tom Almy, Tualatin, Oregon (toma@tekgvs.labs.tek.com) modified the NANSI 
  36. ; version 2.2 code for use in EGA/VGA environments only:
  37. ; 8 Jan 89:   Additional compilation options
  38. ;             Scrolling via reprogramming display start (*MUCH* faster)
  39. ;          INT29 updates display directly if not control character.
  40. ;          Various cleanups
  41. ; Nov 89:     Some bug fixes, customization for various cards enhanced
  42. ;             display modes, better handling of graphic cursor, graphic
  43. ;             characters in 16 color modes are drawn by NNANSI rather
  44. ;             than BIOS (much faster).
  45.  
  46.  
  47. ;------------------------------------------------------------------------
  48.  
  49.     include nnansi_d.asm    ; definitions
  50.  
  51.     ; from nnansi_f.asm
  52.     extrn    f_escape:near, f_in_escape:near
  53.  
  54.     ; from nnansi_p.asm
  55.     extrn    param_end:word
  56. if    key_redef
  57.     extrn    redef_end:word
  58. endif
  59.  
  60.     ; from nnansi_i.asm
  61.     extrn    dosfn0:near
  62.  
  63.     ; to nnansi_p.asm
  64.     public    f_loopdone
  65.     public    f_not_ansi
  66.     public    f_ansi_exit
  67.  
  68.     ; to both nnansi_p.asm and nnansi_f.asm
  69.     public    cur_x, cur_y, max_x, cur_attrib
  70.  
  71.     ; to nnansi_f.asm
  72.     public    xy_to_regs, get_blank_attrib
  73.     public    wrap_flag
  74.     public    cur_parm_ptr
  75.     public    cur_coords, saved_coords, max_y
  76.     public    escvector, string_term
  77.     public    cpr_esc, cprseq
  78.     public    video_mode
  79. if key_redef
  80.     public    lookup
  81. endif
  82.     public    gmode_flag
  83.     public    gcursor
  84.     public    recurse
  85.     public    set_gmode
  86.  
  87.     ; to nnansi_i.asm
  88.     public    req_ptr, break_handler
  89.     public    int_29
  90.     if    takeBIOS + fast
  91.     public    new_vid_bios, old_vid_bios
  92.     endif
  93.  
  94. if out_trans
  95.     ; to all modules
  96.     public    xlate_tab_ptr
  97. endif
  98.  
  99. ;--- seg_cs is the CS: override prefix
  100. ; (assembler forgets cs: on second "xlat dummy_cs_byte")
  101. seg_cs    macro
  102.     db    2eh
  103.     endm
  104.  
  105. ;--- push_all, pop_all ------------------------------------------------
  106. ; Save/restore all user registers.
  107. push_all    macro
  108.     if    is_8088
  109.     push    ax
  110.     push    bx
  111.     push    cx
  112.     push    dx
  113.     push    bp
  114.     push    si
  115.     push    di
  116.     else
  117.     pusha
  118.     endif
  119.     endm
  120.  
  121. pop_all macro
  122.     if    is_8088
  123.     pop    di
  124.     pop    si
  125.     pop    bp
  126.     pop    dx
  127.     pop    cx
  128.     pop    bx
  129.     pop    ax
  130.     else
  131.     popa
  132.     endif
  133.     endm
  134.  
  135. keybuf    struc                ; Used in getchar
  136. len    dw    ?
  137. adr    dw    ?
  138. keybuf    ends
  139.  
  140.  
  141. ABS40    segment at 40h
  142.     org    1ah
  143. buffer_head    dw    ?    ; Used in 'flush input buffer' dos call.
  144. buffer_tail    dw    ?
  145.  
  146.     org    49h
  147. crt_mode    db    ?
  148. crt_cols    dw    ?
  149. crt_len        dw    ?
  150. crt_start    dw    ?
  151. cursor_posn    dw    8 dup (?)
  152. cursor_mode    dw    ?
  153. active_page    db    ?
  154. addr_6845    dw    ?
  155. crt_mode_set    db    ?    ; = 7 only if monochrome display adaptor
  156. crt_palette    db    ?
  157.     org    6ch
  158. timer_low    dw    ?    ; low word of time-of-day counter (18.2 hz)
  159.     org    84h
  160. ega_rows    db    ?    ; #rows-1 on display
  161. ega_points    dw    ?    ; bytes per character
  162.  
  163. ABS40    ends
  164.  
  165.     page
  166.  
  167. CODE    segment word public 'CODE'
  168. assume    cs:CODE, ds:CODE
  169.  
  170.     ; Device Driver Header
  171.  
  172.     org    0
  173.  
  174.     dd    -1            ; next device
  175.     dw    8013h            ; attributes
  176.     dw    strategy        ; request header pointer entry
  177.     dw    interrupt        ; request entry point
  178.     db    'CON'            ; device name (8 char)
  179.     db    5 dup (20h)        ;  ... and 5 blanks)
  180.  
  181.     ; Identification- in case somebody TYPEs the assembled driver
  182.     db    27,'[2J'
  183.     db    "Nansi.sys EGA/VGA"
  184.     ife    is_8088
  185.     db    "(80286)"
  186.     else
  187.     db    "(80x86)"
  188.     endif
  189.     db    '(C) Daniel Kegel, Pasadena, CA 1986. Modified Tom Almy'
  190.     db    13, 10, 26
  191.  
  192.     even
  193. ;----- variable area --------------------
  194. req_ptr label    dword
  195. req_off dw    ?
  196. req_seg dw    ?
  197.  
  198. escvector    dw    0    ; state vector of ESCape sequencor
  199. wrap_flag    db    1    ; 0 = no wrap past line end
  200. video_mode    db    3    ; ROM BIOS video mode (2=BW, 3=color)
  201. max_y        db    24
  202. max_cur_x    label    word    ; used to get both max & cur at once
  203. max_x        db    79    ; line width (79 for 80x25 modes)
  204. cur_coords    label    word
  205. cur_x        db    0    ; cursor position (0 = left edge)
  206. cur_y        db    0    ;          (0 = top edge)
  207. saved_coords    dw    ?    ; holds XY after a SCP escape sequence
  208. string_term    db    0    ; either escape or double quote
  209. cur_attrib    db    7    ; current char attributes
  210. cur_page    db    0    ; current display page
  211. gmode_flag    db    0    ; true if in graphics mode
  212. gcursor        db    initgc    ; true if graphic cursor enabled
  213. recurse        db    0    ; true if in display driver
  214. f_cptr_seg    dw    ?    ; part of fastout write buffer pointer
  215. cur_parm_ptr    dw    ?    ; last byte of parm area now used
  216. port_6845    equ    3d4h
  217. if out_trans
  218. xlate_tab_ptr    dw    ?    ; pointer to output translation table
  219. endif
  220.         if    takeBIOS + fast
  221. old_vid_bios    dd    ?    ; pointer to old video bios routine
  222.         endif
  223.         if    fast
  224. temp_val        dw    0
  225.         endif
  226. no_c_flag    db    0    ; there is no cursor on the screen.
  227.  
  228. brkkeybuf    db    3    ; control C
  229. fnkeybuf    db    ?    ; holds second byte of fn key codes
  230. cpr_buf        db    8 dup (?), '['
  231. cpr_esc        db    1bh    ; descending buffer for cpr function
  232.  
  233. ; following four keybufs hold information about input
  234. ; Storage order determines priority- since the characters making up a function
  235. ; key code must never be separated (say, by a Control-Break), they have the
  236. ; highest priority, and so on.    Keyboard keys (except ctrl-break) have the
  237. ; lowest priority.
  238.  
  239. fnkey    keybuf    <0, fnkeybuf>    ; fn key string (0 followed by scan code)
  240. cprseq    keybuf    <0>        ; CPR string (ESC [ y;x R)
  241. brkkey    keybuf    <0, brkkeybuf>    ; ^C
  242. if key_redef
  243. xlatseq keybuf    <0>        ; keyboard reassignment string
  244. endif
  245.  
  246. ;------ xy_to_regs --------------------------------------------
  247. ; on entry: x in cur_x, y in cur_y
  248. ; on exit:  dx = chars left on line, di = address
  249. ; Alters ax, bx.
  250. xy_to_regs    proc    near
  251.     ; Find number of chars 'till end of line, keep in DX
  252.     mov    ax, max_cur_x
  253.     mov    bx, ax            ; save max_x & cur_x for next block
  254.     xor    ah, ah            ; ax = max_x
  255.     xchg    dx, ax
  256.     mov    al, bh
  257.     xor    ah, ah            ; ax = cur_x
  258.     sub    dx, ax
  259.     inc    dx            ; dx is # of chars till EOL
  260.     ; Calculate DI = current address in text buffer
  261.     mov    al, bl            ; al = max_x
  262.     inc    al
  263.     mul    cur_y
  264.     add    al, bh            ; al += cur_x
  265.     adc    ah, 0            ; AX is # of chars into buffer
  266.     add    ax, ax
  267.     xchg    di, ax            ; DI is now offset of cursor.
  268. if fast
  269.     push    ds
  270.     mov    ax, ABS40
  271.     mov    ds, ax
  272.     assume    ds:ABS40
  273.     add    di, crt_start        ; crt offset
  274.     pop    ds
  275.     assume    ds:nothing
  276. endif
  277.     ret
  278. xy_to_regs    endp
  279.  
  280.  
  281. ;------- dos_fn_tab -------------
  282. ; This table is used in "interrupt" to call the routine that handles
  283. ; the requested function.
  284.  
  285. max_cmd equ    12
  286. dos_fn_tab:
  287.     dw    dosfn0, nopcmd, nopcmd, badcmd, dosfn4, dosfn5, dosfn6
  288.     dw    dosfn7, dosfn8, dosfn8, nopcmd, nopcmd
  289.  
  290. ;------- strategy ----------------------------------------------------
  291. ; DOS calls strategy with a request which is to be executed later.
  292. ; Strategy just saves the request.
  293.  
  294. strategy    proc    far
  295.     mov    cs:req_off,BX
  296.     mov    cs:req_seg,ES
  297.     ret
  298. strategy    endp
  299.  
  300. ;------ interrupt -----------------------------------------------------
  301. ; This is where the request handed us during "strategy" is
  302. ; actually carried out.
  303. ; Calls one of 12 subroutines depending on the function requested.
  304. ; Each subroutine returns with exit status in AX.
  305.  
  306. interrupt    proc    far
  307.  
  308.     sti
  309.     push_all            ; preserve caller's registers
  310.     push    ds
  311.     push    es
  312.     
  313.  
  314.     ; Read requested function information into registers
  315.     lds    bx,cs:req_ptr
  316.     xor    ah,ah            ; clear upper part of ax
  317.     mov    al,ds:[BX+02h]        ; al = function code
  318. ;
  319. ; The next instruction blows up MASM 1.0 but who cares!!
  320. ;
  321.     les    si,[BX+0Eh]        ; ES:SI = input/output buffer addr
  322.     mov    cx,[BX+12h]        ; cx = input/output byte count
  323.  
  324.     cmp    al, max_cmd
  325.     ja    unk_command        ; too big, exit with error code
  326.  
  327.     xchg    bx, ax
  328.     shl    bx, 1            ; form index to table of words
  329.     mov    ax, cs
  330.     mov    ds, ax
  331.     call    word ptr dos_fn_tab[bx]
  332. int_done:
  333.     lds    bx,cs:req_ptr        ; report status
  334.     or    ax, 100h        ; (always set done bit upon exit)
  335.     mov    [bx+03],ax
  336.  
  337.     pop    ES            ; restore caller's registers
  338.     pop    DS
  339.     pop_all
  340.     ret                ; return to DOS.
  341.  
  342. unk_command:
  343.     call    badcmd
  344.     jmp    int_done
  345.  
  346. interrupt    endp
  347.  
  348. ;----- BIOS break handler -----------------------------------------
  349. ; Called by BIOS when Control-Break is hit (vector was set up in Init).
  350. ; Simply notes that a break was hit.  Flag is checked during input calls.
  351.  
  352. break_handler    proc
  353.     mov    cs:brkkey.len, 1
  354.     iret
  355. break_handler    endp
  356.  
  357.     page
  358.  
  359. ;------ badcmd -------------------------------------------------------
  360. ; Invalid function request by DOS.
  361. badcmd    proc    near
  362.     mov    ax, 813h        ; return "Error: invalid cmd"
  363.     ret
  364. badcmd    endp
  365.  
  366.  
  367. ;------ nopcmd -------------------------------------------------------
  368. ; Unimplemented or dummy function request by DOS.
  369. nopcmd    proc    near
  370.     xor    ax, ax            ; No error, not busy.
  371.     ret
  372. nopcmd    endp
  373.  
  374. ;------- dos function #4 ----------------------------------------
  375. ; Reads CX characters from the keyboard, places them in buffer at
  376. ; ES:SI.
  377. dosfn4    proc    near
  378.     jcxz    dos4done
  379.     mov    di, si
  380. dos4lp: push    cx
  381.     call    getchar
  382.     pop    cx
  383.     stosb
  384.     loop    dos4lp
  385. dos4done:
  386.     xor    ax, ax            ; No error, not busy.
  387.     ret
  388. dosfn4    endp
  389.  
  390. ;-------- dos function #5: non-destructive input, no wait ------
  391. ; One-character lookahead into the keyboard buffer.
  392. ; If no characters in buffer, return BUSY; otherwise, get value of first
  393. ; character of buffer, stuff into request header, return DONE.
  394. dosfn5    proc    near
  395.     call    peekchar
  396.     jz    dos5_busy
  397.  
  398.     lds    bx,req_ptr
  399.     mov    [bx+0Dh], al
  400.     xor    ax, ax            ; No error, not busy.
  401.     jmp    short dos5_exit
  402. dos5_busy:
  403.     MOV    ax, 200h        ; No error, busy.
  404. dos5_exit:
  405.     ret
  406.  
  407. dosfn5    endp
  408.  
  409. ;-------- dos function #6: input status --------------------------
  410. ; Returns "busy" if no characters waiting to be read.
  411. dosfn6    proc    near
  412.     call    peekchar
  413.     mov    ax, 200h        ; No error, busy.
  414.     jz    dos6_exit
  415.     xor    ax, ax            ; No error, not busy.
  416. dos6_exit:
  417.     ret
  418. dosfn6    endp
  419.  
  420. ;-------- dos function #7: flush input buffer --------------------
  421. ; Clears the IBM keyboard input buffer.     Since it is a circular
  422. ; queue, we can do this without knowing the beginning and end
  423. ; of the buffer; all we need to do is set the tail of the queue
  424. ; equal to the head (as if we had read the entire queue contents).
  425. ; Also resets all the device driver's stuffahead buffers.
  426. dosfn7    proc    near
  427.     xor    ax, ax
  428.     mov    fnkey.len, ax        ; Reset the stuffahead buffers.
  429.     mov    cprseq.len, ax
  430.     mov    brkkey.len, ax
  431. if key_redef
  432.     mov    xlatseq.len, ax
  433. endif
  434.  
  435.     mov    ax, ABS40
  436.     mov    es, ax
  437.     mov    ax, es:buffer_head    ; clear queue by making the tail
  438.     mov    es:buffer_tail, ax    ; equal to the head
  439.  
  440.     xor    ax, ax            ; no error, not busy.
  441.     ret
  442. dosfn7    endp
  443.  
  444.     page
  445.     if    takeBIOS + fast
  446. ;--- new_vid_bios -------------------------------------------
  447. ; New_vid_bios simply replaces the write_tty call.
  448. ; All other calls get sent to the old video bios.
  449. ; This gives BIOS ANSI capability.
  450. ; However, it takes away the escape character.
  451. ; If this is not desired, just tell init to not take over the vector.
  452.  
  453. ; IF fast is used, then clear screen is also captured
  454.  
  455.  
  456. new_vid_bios    proc
  457. if takeBIOS
  458.     cmp    ah, 14
  459.     jz    nvb_write_tty
  460. endif
  461.     cmp    cs:recurse,0        ; if recursive, do nothing
  462.     jnz    new_vid_pass
  463.     cmp    Ah, 02h ; set cursor position
  464.     jnz    nvb_notsetcursor
  465.     jmp    nvb_setcursor
  466. nvb_notsetcursor:
  467. if fast
  468.     cmp    Ah, 06h    ; clear screen call
  469.     jz    nvb_scroll
  470.     cmp    Ah, 07h    ; alternative call
  471.     jz    nvb_scroll
  472.     cmp    Ah, 0Fh ; get display mode
  473.     jz    nvb_display
  474. endif
  475.     cmp    ah,0    ; change mode command?
  476.     jnz    new_vid_pass
  477.     jmp    nvb_smode
  478. new_vid_pass:
  479.     jmp    dword ptr cs:old_vid_bios
  480. if takeBIOS
  481. nvb_write_tty:
  482.     sti
  483.     push    ds
  484.     push    cs
  485.     pop    ds    ; establish adressability
  486.     assume    ds:CODE
  487.     push    es
  488.     push_all
  489.     mov    cl, cur_attrib
  490.     ; If in graphics mode, BL is new color
  491.     cmp    gmode_flag, 0
  492.     jz    nvb_wt_text
  493.  
  494.     mov    cur_attrib, bl    ; ja?
  495. nvb_wt_text:
  496.     push    cx
  497.  
  498.     mov    cx, 1
  499.     mov    bx, cs
  500.     mov    es, bx
  501.     mov    si, offset int_29_buf
  502.     mov    byte ptr es:[si], al
  503.     call    dosfn8
  504.  
  505.     pop    cx
  506.     mov    cur_attrib, cl    ; restore color
  507.     pop_all
  508.     pop    es
  509.     pop    ds
  510.     assume    ds:nothing
  511.     iret
  512. endif
  513. if fast
  514. nvb_display:
  515.     push    ds
  516.     push    dx
  517.     mov    dx, ABS40
  518.     mov    ds, dx
  519.     assume    ds:ABS40
  520.     cmp    cs:gmode_flag,0    ; Graphic mode?
  521.     jnz    nvb_passx
  522.     cmp    crt_start,0    ; At start of mem?
  523.     jnz    nvb_reset
  524. nvb_passx:
  525.     jmp    nvb_pass
  526. nvb_scroll:
  527.     push    ds
  528.     push    dx
  529.     mov    dx, ABS40
  530.     mov    ds, dx
  531.     assume    ds:ABS40
  532.     mov    cs:no_c_flag, 1    ; if graphic, don't draw cursor afterwards
  533.     cmp    cs:gmode_flag,0    ; graphic mode?
  534.     jnz    nvb_gpass
  535.     cmp    crt_start,0    ; not at start of mem
  536.     jz    nvb_pass
  537.     cmp    al, 0        ; scroll, not erase
  538.     jne    nvb_pass
  539.     or    cx, cx        ; not entire screen?
  540.     jne    nvb_pass
  541.     pop    dx
  542.     push    dx
  543.     inc    dl
  544.     cmp    dl, byte ptr crt_cols    ; same question
  545.     jne    nvb_pass
  546.     cmp    dh, ega_rows
  547.     jne    nvb_pass
  548.     push    ax        ; erase is easier since we dont move screen
  549.     xor    ax,ax
  550.     mov    crt_start,0    ; reset offsets
  551.     mov    dx,port_6845
  552.     mov    al,0ch
  553.     out    dx,ax
  554.     inc    al
  555.     out    dx,ax
  556.     pop    ax
  557.     jmp    nvb_pass
  558.  
  559. nvb_reset:    ; reset display and then pass command on
  560.     push_all
  561.     push    es
  562.     call    move_back
  563.     pop    es
  564.     pop_all
  565.     jmp    nvb_pass
  566. endif
  567. nvb_gpass:    ; do command and then reposition cursor
  568.     assume    ds:ABS40
  569.     cmp    cs:no_c_flag, 0
  570.     mov    cs:no_c_flag, 0
  571.     jnz    nvb_pass
  572.     cmp    cs:gcursor,0        ; graphic cursor being used?
  573.     jz    nvb_pass        ; no-- quit
  574.     mov    cs:recurse,1        ; say we are recursive
  575.     pop    dx            ; restore registers
  576.     pop    ds
  577.     int    10h            ; do the int
  578.     push_all
  579. if quick_char
  580.     mov    ax, 8f16h
  581.     call    quick_graph
  582. else
  583.     mov    ax, 0916h        ; draw cursor at location
  584.     mov    bx, 8fh
  585.     mov    cx, 1
  586.     int    10h
  587. endif
  588.     pop_all
  589.     mov    cs:recurse,0
  590.     assume    ds:nothing
  591.     iret                ; return from interrupt
  592. nvb_pass:
  593.     pop    dx
  594.     assume    ds:nothing
  595.     pop    ds
  596.     jmp    dword ptr cs:old_vid_bios    ; now doit
  597.  
  598. nvb_setcursor:
  599.     push    ds
  600.     push    dx
  601.     mov    dx, ABS40
  602.     mov    ds,dx
  603.     assume    ds:ABS40
  604.     cmp    cs:gmode_flag,0    ; Alpha mode?
  605.     je    nvb_pass
  606.     cmp    cs:no_c_flag, 0    ; inhibited cursor?
  607.     mov    cs:no_c_flag, 0    ; (We need a test and set instruction)
  608.     jnz    nvb_pass
  609.     cmp    cs:gcursor, 0    ; no cursor?
  610.     jz    nvb_pass
  611.     mov    cs:recurse, 1    ; say we are recursing
  612.     push_all
  613. if quick_char
  614.     mov    ax, 8f16h
  615.     call    quick_graph
  616. else
  617.     mov    ax, 0916h        ; draw cursor at location
  618.     mov    bx, 8fh
  619.     mov    cx, 1
  620.     int    10h
  621. endif
  622.     pop_all
  623.     jmp    nvb_gpass    ; do command, then reposition cursor
  624.  
  625. nvb_smode:
  626.     mov    cs:recurse, 1
  627.     int    10h
  628.     push_all
  629.     push    ds
  630.     mov    dx, ABS40
  631.     mov    ds, dx
  632.     assume    ds:ABS40
  633.     mov    al, crt_mode    ; get mode and check for being graphic
  634.     call    set_gmode
  635.     mov    cs:no_c_flag, al ; if graphic, then no cursor is on screen.
  636.     pop    ds
  637.     assume    ds:nothing
  638.     pop_all
  639.     iret
  640.  
  641. new_vid_bios    endp
  642.     endif
  643.  
  644. ;------ int_29 ----------------------------------------------
  645. ; Int 29 handles DOS quick-access putchar.
  646. ; Last device loaded with attribute bit 4 set gets accessed for
  647. ; single-character writes via int 29h instead of via interrupt.
  648. ; Must preserve all registers.
  649. ; Installed as int 29h by dosfn0 (init).
  650. int_29_buf    db    ?
  651.  
  652. int_29    proc    near
  653.     sti
  654.     push    ds
  655.     push    es
  656.     push_all
  657. if fast29
  658.     cmp    al, 20h            ; control char?
  659.     jb    slow_way
  660.     cmp    cs:escvector, 0        ; middle of an escape sequence?
  661.     jnz    slow_way
  662.     mov    dx, ABS40
  663.     mov    ds, dx            ; set addressability
  664.     assume    ds:ABS40
  665.     mov    cx, word ptr crt_mode    ; mode in cl, columns in ch
  666.     cmp    cl, 3            ; graphics mode?
  667.     ja    slow_way
  668.     mov    dx, cursor_posn        ; dh has y, dl has x
  669.     inc    dl            ; point to next location
  670.     cmp    dl, ch            ; at edge?
  671.     jnb    slow_way
  672.                     ; we can go with it!
  673.     mov    cursor_posn, dx        ; update pointer
  674.     xchg    ax, bx
  675.     mov    al, dh
  676.     mul    ch            ; ax has line offset
  677.     add    al, dl
  678.     adc    ah, 0            ; total offset
  679.     mov    cx, bx
  680.     mov    bx, ax            ; cl has character, bx offset
  681. if fast
  682.     mov    ax, crt_start
  683.     shr    ax, 1
  684.     add    bx, ax            ; corrected cursor offset
  685. endif
  686.     mov    dx, port_6845        ; update cursor location
  687.     mov    al,0eh        ; more effective to write two bytes at a time
  688.     mov    ah,bh
  689.     out    dx,ax
  690.     inc    al
  691.     mov    ah,bl
  692.     out    dx,ax
  693.  
  694.     mov    ax, cx        ; do character translation
  695. if out_trans
  696.     xchg    bx, cx
  697.     mov    bx, cs:xlate_tab_ptr
  698.     seg_cs
  699.     xlat
  700.     xchg    bx,cx
  701. endif
  702.     dec    bx
  703.     add    bx, bx            ; byte offset
  704.     mov    dx, 0b800h        ; address screen
  705.     mov    ds, dx
  706.     assume    ds:nothing
  707.     mov    ah, cs:cur_attrib
  708.     mov    ds:[bx], ax        ; write character
  709.     jmp    short int_fin
  710. endif
  711. slow_way:
  712.     mov    cx, 1
  713.     mov    bx, cs
  714.     mov    es, bx
  715.     mov    si, offset int_29_buf
  716.     mov    byte ptr es:[si], al
  717.     call    dosfn8
  718. int_fin:
  719.     pop_all
  720.     pop    es
  721.     pop    ds
  722.     iret
  723. int_29    endp
  724.  
  725.  
  726.     page
  727. ;------ dosfn8 -------------------------------------------------------
  728. ; Handles writes to the device (with or without verify).
  729. ; Called with
  730. ;  CX     = number of bytes to write
  731. ;  ES:SI = transfer buffer
  732. ;  DS     = CS, so we can access local variables.  NOT ANY MORE
  733.  
  734. dosfn8    proc    near
  735.  
  736.     mov    cs:f_cptr_seg, es    ; save segment of char ptr
  737.  
  738.     ; Read the BIOS buffer address/cursor position variables.
  739.     mov    ax, ABS40
  740.     mov    ds, ax
  741.     assume    ds:ABS40
  742.  
  743.     ; Find current video mode and screen size.
  744.     mov    ax,word ptr crt_mode    ; al = crt mode; ah = # of columns
  745.     mov    cs:video_mode, al
  746.     dec    ah            ; ah = max column
  747.     mov    cs:max_x, ah
  748.  
  749.     ; Save graphics mode flag
  750.     call    set_gmode
  751.  
  752.     mov    al, ega_rows        ; number of display rows
  753.     mov    cs:max_y, al        ; set maxy value
  754.  
  755.     ; Find current cursor coordinates.
  756. if multi_page
  757.     mov    al,active_page
  758.     cbw
  759.     add    ax,ax
  760.     xchg    bx,ax
  761.     mov    ax,cursor_posn[bx]
  762. else
  763.     mov    ax,cursor_posn
  764. endif
  765.     mov    cs:cur_coords,AX
  766.  
  767.     ; Find video buffer segment address; adjust it
  768.     ; so the offset is zero; return in AX.
  769.  
  770.     mov    ax, 0B800H
  771.  
  772.     push    cs
  773.     pop    ds
  774.     assume    ds:CODE
  775.     mov    es, ax
  776.     call    xy_to_regs        ; Set DX, DI according to cur_coords.
  777.  
  778.     ; | If in graphics mode, clear old pseudocursor
  779.  
  780.     cmp    gmode_flag, 0
  781.     jz    d8_no_cp
  782.     cmp    no_c_flag, 0
  783.     mov    no_c_flag, 0
  784.     jnz    d8_no_cp
  785.     cmp    gcursor, 0    ; unless graphics cursor is disabled
  786.     jz    d8_no_cp
  787.     push    cx
  788. if quick_char
  789.     push    dx
  790.     mov    ax, 8f16h
  791.     call    quick_graph
  792.     pop    dx
  793. else
  794.     push    di
  795.     mov    ax, 0916h    ; xor, color 15, ^V (small block)
  796.     mov    bx, 8fh
  797.     mov    cx, 1
  798.     int    10h
  799.     pop    di
  800. endif
  801.     pop    cx
  802.  
  803. d8_no_cp:
  804.  
  805.     mov    recurse, 1    ; protect against funny happenings
  806.  
  807.     
  808. if out_trans
  809.     mov    bx, xlate_tab_ptr    ; get pointer to translation table
  810. endif
  811.     mov    ah, cur_attrib
  812.     mov    ds, f_cptr_seg        ; get segment of char ptr
  813.     assume    ds:nothing
  814.     cld                ; make sure we'll increment
  815.  
  816.     ; The Inner Loop: 12+4+4+11+14+2+19= 66 cycles/loop
  817.     ; on 8088; at 4.77 MHz, that gives 16.1 microseconds/loop.
  818.     ; At that speed, it takes 32 milliseconds to fill a screen.
  819.  
  820.     ; Get a character, put it on the screen, repeat 'til end of line
  821.     ; or no more characters.
  822.     jcxz    f_loopdone        ; if count = 0, we're already done.
  823.     cmp    cs:escvector, 0        ; If in middle of an escape sequence,
  824.     jz    f_tloop
  825.     jmp    f_in_escapex        ; jump to escape sequence handler.
  826.  
  827. f_tloop:; | If in graphics mode, jump to alternate loop
  828.     ; | What a massive kludge!  A better approach would have been
  829.     ; | to collect characters for a "write n chars" routine
  830.     ; | which would handle both text and graphics modes.
  831.     cmp    cs:gmode_flag,0
  832.     jz    f_t_cloop
  833.  
  834.     jmp    f_g_cloop
  835.  
  836. f_t_cloop:
  837.     LODSB                ; get char! (al = ds:[si++])
  838.     cmp    al, 28            ; is it a control char?
  839.     jb    f_control        ;  maybe...
  840. f_t_nctl:
  841. if out_trans
  842.     seg_cs
  843.     xlat
  844. endif
  845.     STOSW                ; Put Char! (es:[di++] = ax)
  846.     dec    dx            ; count down to end of line
  847.     loopnz    f_t_cloop        ; and go back for more.
  848.     jz    f_t_at_eol        ; at end of line; maybe do a crlf.
  849.     jmp    short f_loopdone
  850.  
  851. ; placed here so addresses resolve
  852. pseudocursor:
  853.     cmp    cs:gcursor, 0    ; graphics cursor off?
  854.     jz    nopseudo
  855. if quick_char
  856.     mov    ax, 8f16h
  857.     call    quick_graph
  858. else
  859.     mov    ax, 0916h    ; xor, color 15, ^V (small block)
  860.     mov    bx, 8fh
  861.     mov    cx, 1
  862.     int    10h
  863. endif
  864. nopseudo:
  865.     xor    ax, ax
  866.     ret
  867.  
  868. f_looploop:
  869. f_ansi_exit:                ; in case we switched into
  870.     loopnz    f_tloop            ; a graphics mode
  871.     jnz    f_loopdone
  872. f_t_at_eol:
  873.     jmp    f_at_eol
  874.  
  875. f_loopdone:
  876.  
  877.     ;--------- All done with write request -----------
  878.     ; DI is cursor address, cursor position in cur_y, dl
  879.  
  880.     assume    ds:ABS40
  881.     mov    ax, ABS40
  882.     mov    ds, ax
  883.  
  884.     mov    cs:recurse, 0        ; we are leaving now
  885.  
  886.     ; Set cursor position in low memory.
  887. ; Does anybody ever use anything but page zero?
  888. if multi_page
  889.     mov    al,active_page
  890.     cbw
  891.     add    ax,ax
  892.     xchg    bx,ax
  893.     mov    al, cs:max_x
  894.     inc    al
  895.     sub    al, dl
  896.     mov    ah, cs:cur_y
  897.     mov    cursor_posn[bx],ax
  898. else
  899.     mov    al, cs:max_x
  900.     inc    al
  901.     sub    al, dl
  902.     mov    ah, cs:cur_y
  903.     mov    cursor_posn,ax
  904. endif
  905.     cmp    cs:gmode_flag,0
  906.     jnz    pseudocursor
  907.  
  908.     ; Write directly to 6845 cursor address register.
  909.     mov    bx, di
  910.     shr    bx, 1            ; convert word index to byte index
  911.  
  912.     mov    dx, port_6845    ; works with or without no-mono
  913.  
  914.     mov    al,0eh        ; more effective to write two bytes at a time
  915.     mov    ah,bh
  916.     out    dx,ax
  917.     inc    al
  918.     mov    ah,bl
  919.     out    dx,ax
  920.  
  921.  
  922.     ; Return to DOS.
  923.     xor    ax, ax            ; No error, not busy.
  924.     ret
  925.  
  926.     ;---- handle control characters ----
  927.     ; Note: cur_x is not kept updated in memory, but can be
  928.     ; computed from max_x and dx.
  929.     ; Cur_y is kept updated in memory.
  930. f_control:
  931.     cmp    al, 27            ; Is it an escape?
  932.     jz    f_escapex
  933.     cmp    al, 13            ; carriage return?
  934.     jz    f_cr
  935.     cmp    al, 10            ; line feed?
  936.     jz    f_lf
  937.     cmp    al, 9            ; tab?
  938.     jz    f_tabx
  939.     cmp    al, 8            ; backspace?
  940.     jz    f_bs
  941.     cmp    al, 7            ; bell
  942.     jz    f_bell
  943.     jmp    f_nctl            ; then it is not a control char.
  944.  
  945. f_tabx: jmp    f_tab
  946. f_escapex:
  947.     jmp    f_escape
  948. f_in_escapex:
  949.     jmp    f_in_escape
  950.  
  951. f_bs:    ;----- Handle backspace -----------------
  952.     ; Moves cursor back one space without erasing.    No wraparound.
  953.     cmp    dl, cs:max_x        ; wrap around to previous line?
  954.     ja    fbs_wrap        ; yep; disallow it.
  955.     dec    di            ; back up one char & attrib,
  956.     dec    di
  957.     inc    dx            ; and note one more char left on line.
  958. fbs_wrap:
  959.     jmp    f_looploop
  960.  
  961. f_bell: ;----- Handle bell ----------------------
  962.     ; Use BIOS to do the beep.  DX is not changed, as bell is nonprinting.
  963.     call    beep
  964.     or    al, al            ; clear z
  965.     jmp    f_looploop        ; Let main loop decrement cx.
  966.  
  967. f_cr:    ;----- Handle carriage return -----------
  968.     ; di -= cur_x<<1;        set di= address of start of line
  969.     ; dx=max_x+1;            set bx= chars left in line
  970.     mov    al, cs:max_x
  971.     inc    al
  972.     sub    al, dl            ; Get cur_x into ax.
  973.     mov    ah, 0
  974.     sub    di, ax
  975.     sub    di, ax
  976.     mov    dl, cs:max_x        ; Full line ahead of us.
  977.     inc    dx
  978.     mov    ah, cs:cur_attrib    ; restore current attribute
  979.     or    al, 1            ; clear z
  980.     jmp    f_looploop        ; and let main loop decrement cx
  981.  
  982. f_at_eol:
  983.     ;----- Handle overrunning right end of screen -------
  984.     ; cx++;                compensate for double loop
  985.     ; if (!wrap_flag) { dx++; di-=2; }
  986.     ; else do_crlf;
  987.     inc    cx
  988.     test    cs:wrap_flag, 1
  989.     jnz    feol_wrap
  990.         dec    di
  991.         dec    di
  992.         inc    dx
  993.         jmp    f_looploop
  994. feol_wrap:
  995.     ; dx=max_x+1;            set bx= chars left in line
  996.     ; di -= 2*(max_x+1);
  997.     ; do_lf
  998.     mov    dl, cs:max_x
  999.     inc    dx
  1000.     sub    di, dx
  1001.     sub    di, dx
  1002.     ; fall thru to line feed routine
  1003.  
  1004. f_lf:    ;----- Handle line feed -----------------
  1005.     ; if (cur_y >= max_y) scroll;        scroll screen up if needed
  1006.     ; else { cur_y++; di += max_x<<1;    else increment Y
  1007.  
  1008.     mov    al, cs:max_y
  1009.     cmp    cs:cur_y, al
  1010.     jb    flf_noscroll
  1011.         call    scroll_up        ; preserves bx,cx,dx,si,di
  1012.         jmp    short flf_done
  1013. flf_noscroll:
  1014.     inc    cs:cur_y
  1015.     mov    al, cs:max_x
  1016.     mov    ah, 0
  1017.     inc    ax
  1018.     add    ax, ax
  1019.     add    di, ax
  1020. flf_done:
  1021.     mov    ah, cs:cur_attrib        ; restore current attribute
  1022.     or    al, 1            ; clear z
  1023.     jmp    f_looploop        ; and let main loop decrement cx
  1024.  
  1025. f_tab:    ;----- Handle tab expansion -------------
  1026.     ; Get cur_x into al.
  1027.     mov    al, cs:max_x
  1028.     inc    al
  1029.     sub    al, dl
  1030.     ; Calculate number of spaces to output.
  1031.     push    cx            ; save cx
  1032.     mov    ch, 0
  1033.     mov    cl, al            ; get zero based x coordinate
  1034.     and    cl, 7
  1035.     neg    cl
  1036.     add    cl, 8            ; 0 -> 8, 1 -> 8, ... 7 -> 1
  1037.     sub    dx, cx            ; update chars-to-eol, maybe set z
  1038.     pushf                ; || save Z for main loop
  1039.     ; ah is still current attribute.  Move CX spaces to the screen.
  1040.     mov    al, ' '
  1041.     cmp    cs:gmode_flag,0
  1042.     jnz    f_tab_putc
  1043.  
  1044.     REP    STOSW
  1045.     popf                ; || restore Z flag for main loop test
  1046.     pop    cx            ; restore cx
  1047.     jmp    f_looploop        ; Let main loop decrement cx.
  1048.  
  1049. ;--------------- graphics mode support -----------------------
  1050.  
  1051. f_tab_putc:    ; graphics mode- call putc to put the char
  1052.     add    dx, cx            ; move back to start of tab
  1053. f_tp_lp:
  1054.     call    putchar
  1055.     dec    dx            ; go to next cursor position
  1056.     loop    f_tp_lp
  1057.     popf                ; Z set if wrapped around EOL
  1058.     pop    cx
  1059.     jmp    f_looploop
  1060.  
  1061. ;---- Where to go when a character turns out not to be special
  1062. f_nctl:
  1063. f_not_ansi:
  1064.     cmp    cs:gmode_flag,0
  1065.     jnz    f_g_nctl
  1066. f_jmptnctl:
  1067.     jmp    f_t_nctl        ; text mode
  1068.  
  1069. ;---- Alternate main loop for graphics mode ----
  1070. f_g_cloop:
  1071.     LODSB                ; get char! (al = ds:[si++])
  1072.     cmp    al, 28            ; is it a control char?
  1073.     jb    f_g_control        ;  maybe...
  1074. f_g_nctl:
  1075. if out_trans
  1076.     seg_cs
  1077.     xlat
  1078. endif
  1079.     call    putchar
  1080.     dec    dx            ; count down to end of line
  1081.     loopnz    f_g_cloop        ; and go back for more.
  1082.     jz    f_g_at_eol        ; at end of line; maybe do a crlf.
  1083.     jmp    f_loopdone
  1084.  
  1085. f_g_control:    jmp    f_control
  1086. f_g_at_eol:    jmp    f_at_eol
  1087.  
  1088. ;---- putchar ------------------------------------------------
  1089. ; Writes char AL, attribute AH to screen at (max_x+1-dl), cur_y.
  1090. ; On entry, registers set up as per xy_to_regs.
  1091. ; Preserves all registers.
  1092. putchar proc    near
  1093.     push    dx
  1094.     push    cx
  1095.     push    bx
  1096.     push    ax
  1097.     ; 1. Set cursor position.
  1098.     mov    al, cs:max_x
  1099.     inc    al
  1100.     sub    al, dl
  1101.     mov    cs:cur_x, al
  1102.     mov    dx, cs:cur_coords    ; get X & Y into DX
  1103.     push    ds
  1104.     mov    ax, 40h
  1105.     mov    ds, ax
  1106.     assume    ds:ABS40
  1107.     mov    cursor_posn,dx
  1108.     pop    ds
  1109.     assume    ds:nothing
  1110.     xor    bx, bx            ; choose dpy page 0
  1111.     mov    ah, 2            ; chose "Set Cursor Position"
  1112.     int    10h            ; call ROM BIOS
  1113.     ; 2. Write char & attribute.
  1114. IF quick_char
  1115.     pop    ax
  1116.     push    ax            ; character and attribute
  1117.     call    quick_graph
  1118. ELSE
  1119.     mov    cx, 1
  1120.     pop    ax            ; get char in AL
  1121.     push    ax
  1122.     mov    bl, ah            ; attribute in BL
  1123.     mov    bh, 0
  1124.     mov    ah, 9
  1125.     int    10h
  1126. ENDIF
  1127.     pop    ax
  1128.     pop    bx
  1129.     pop    cx
  1130.     pop    dx
  1131.     ret
  1132. putchar endp
  1133.  
  1134. IF quick_char
  1135. quick_graph    proc    near
  1136. ; ah= mode, al= char, ax,bx,cx,dx destroyed
  1137.     gmode_test yesQuick
  1138.  
  1139.     mov    bl,ah
  1140.     xor    bh,bh
  1141.     mov    cx, 1
  1142.     mov    ah, 9
  1143.     int    10h            ; do it the old way
  1144.     ret
  1145.  
  1146. yesQuick:
  1147.     push    ds
  1148.     mov    bx, 40h
  1149.     mov    ds, bx
  1150.     assume    ds:ABS40        ; address abs segment
  1151.     push    es
  1152.     push    bp
  1153.     push    si
  1154.     push    di            ; save some registers
  1155.     push    ax            ; save char and mode
  1156.     
  1157.     mov    ax, crt_cols
  1158.     mov    cx, ega_points        ; pixel rows in character
  1159.     mov    bp, ax            ; save number of columns=#bytes
  1160.     mul    byte ptr (cursor_posn+1)
  1161.     mul    cx            ; (ignore upper product in DX)
  1162.     add    al, byte ptr (cursor_posn)    ; y*#cols*#rows + x
  1163.     adc    ah, 0            ; take care of carry
  1164.     mov    si, ax            ; save address in si
  1165.     xor    ax, ax
  1166.     mov    es, ax            ; absolute zero
  1167.     les    di, es: dword ptr (43h * 4)    ; contents of vector 43h
  1168.     pop    ax
  1169.     push    ax            ; get char and mode
  1170.     mul    cl            ; offset to character in table
  1171.     add    di,ax            ; di has character bit pattern start
  1172.     mov    ax, 0a000h        ; address of display segment
  1173.     mov    ds, ax
  1174.     assume    ds:nothing
  1175.  
  1176. ; to recap: cx=#rows, bp=#columns, ds:si=display address, es:di=character addr
  1177.     mov    dx, 3ceh
  1178.     mov    ax, 0a05h
  1179.     out    dx,ax            ; set write mode 2, read mode 1
  1180.     pop    ax
  1181.     push    ax            ; get character mode in ah
  1182.     or    ax,ax
  1183.     mov    ax, 0
  1184.     jns    overMode
  1185.     mov    ax, 1803h
  1186. overMode:
  1187.     out    dx,ax
  1188.     mov    ax, 7
  1189.     out    dx,ax
  1190.  
  1191.     pop    bx            ; character mode in bh
  1192.     and    bx, 7f00h        ; mask off xor bit
  1193.     mov    al, 8
  1194. chLoop:
  1195.     mov    ah, es:[di]        ; get pixel pattern
  1196.     out    dx, ax
  1197.     and    [si],bh            ; update foreground
  1198.     not    ah
  1199.     out    dx, ax                 ; and background
  1200.     and    [si],bl
  1201.     inc    di
  1202.     add    si, bp            ; go to next character byte and line
  1203.     loop    chLoop
  1204.     
  1205.     mov    ax, 0ff08h        ; bit mask
  1206.     out    dx, ax
  1207.     mov    ax, 5            ; mode register
  1208.     out    dx, ax
  1209.     mov    ax, 3
  1210.     out    dx, ax
  1211.     mov    ax, 0f07h
  1212.     out    dx, ax
  1213.  
  1214.     pop    di
  1215.     pop    si
  1216.     pop    bp
  1217.     pop    es
  1218.     pop    ds
  1219.     ret
  1220. quick_graph endp
  1221. ENDIF
  1222.  
  1223.  
  1224. ;--------------- end of graphics mode support --------------------
  1225.  
  1226. dosfn8    endp
  1227.  
  1228. ;--- get_blank_attrib ------------------------------------------------
  1229. ; Determine new attribute and character for a new blank region.
  1230. ; Use current attribute, just disallow blink and underline.
  1231. ; (Pretty strange way to do it.     Might want to disallow rev vid, too.)
  1232. ; Returns result in AH, preserves all other registers.
  1233. get_blank_attrib    proc    near
  1234.     mov    ah, 0            ; 0 is background if graphics mode
  1235.     cmp    cs:gmode_flag,0
  1236.     jnz    gb_aok
  1237.  
  1238.     mov    ah, cs:cur_attrib
  1239.     and    ah, 7fh            ; disallow blink
  1240.  
  1241. gb_aok: ret
  1242. get_blank_attrib    endp
  1243.  
  1244.  
  1245. ;---- scroll_up ---------------------------------------------------
  1246. ; Scroll screen up- preserves ax, bx, cx, dx, si, di, ds, es.
  1247. ; Moves screen up 1 line, fills the last line with blanks.
  1248. ; Attribute of blanks is the current attribute sans blink and underline.
  1249.  
  1250. scroll_up    proc    near
  1251.     push_all
  1252.  
  1253.     cmp    cs:gmode_flag,0
  1254.     jnz    scroll_graphic
  1255. if fast
  1256.     push    es
  1257.     push    ds            ; save all!
  1258.     mov    ax, 0b800h        ; address display via es
  1259.     mov    es, ax
  1260.     mov    ax, ABS40        ; and low mem via ds
  1261.     mov    ds, ax
  1262.     assume    ds:ABS40
  1263.     
  1264.     xor    ax,ax            ; calc addresses
  1265.     mov    al, cs:max_x
  1266.     inc    ax
  1267.     mov    cx, ax            ; save (word) count for fill
  1268.     mov    bx, ax            ; and save byte count
  1269.     shl    bx, 1            ; byte count
  1270.     mov    cs:temp_val, bx
  1271.     mul    cs:max_y        ; address offset of last line (words)
  1272.     shl    ax, 1            ; address offset in bytes
  1273.     mov    di, ax
  1274.     
  1275.     mov    ax, crt_start        ; start of display
  1276.     add    ax, bx            ; add line size in bytes
  1277.     add    di, ax            ; di is now address of new last line
  1278.     cmp    di, 7fffh - 264        ; is there room here?
  1279.     ja    no_room_here
  1280.  
  1281.     mov    crt_start, ax
  1282.     shr    ax, 1            ; make into word offset
  1283.     mov    bx, ax            ; and put into 6845
  1284.     mov    dx, port_6845
  1285.     mov    al, 0ch
  1286.     out    dx, ax
  1287.     inc    al
  1288.     mov    ah, bl
  1289.     out    dx, ax
  1290.     
  1291.     mov    ah, cs:cur_attrib
  1292.     and    ah, 7fh            ; disallow blink
  1293.     mov    al, 20h            ; blank
  1294.     rep stosw            ; clear line
  1295.  
  1296.     assume    ds:nothing
  1297.     pop    ds
  1298.     pop    es
  1299.     pop_all
  1300.     
  1301.     add    di, cs:temp_val
  1302.     ret
  1303.  
  1304. no_room_here:
  1305.     call    move_back        ; go to buffer start
  1306.     pop    ds            ; restore registers
  1307.     pop    es
  1308.     pop_all
  1309.     sub    di, cs:temp_val
  1310.     jmp    scroll_up        ; try again
  1311.  
  1312. else
  1313.     push    es
  1314.     push    ds            ; save all!
  1315.     mov    ax, 0b800h        ; address display
  1316.     mov    ds, ax
  1317.     mov    es, ax
  1318.     xor    di,di
  1319.     xor    ax,ax            ; calc addresses
  1320.     mov    al, cs:max_x
  1321.     inc    ax
  1322.     mov    bx, ax            ; save (word) count
  1323.     shl    ax, 1            ; byte count
  1324.     mov    si, ax            ; start address is second line
  1325.     mov    ax, bx
  1326.     mul    cs:max_y        ; number of words to move
  1327.     mov    cx, ax
  1328.     rep movsw            ; move them!
  1329.     mov    cx, bx            ; words to clear
  1330.           mov    ah, cs:cur_attrib
  1331.     and    ah, 7fh            ; disallow blink
  1332.     mov    al, 20h            ; blank
  1333.     rep stosw            ; clear line
  1334.     pop    ds
  1335.     pop    es
  1336.     pop_all
  1337.     ret
  1338. endif
  1339. scroll_graphic:
  1340.     
  1341.     gmode_test scrOurself
  1342.     mov    bh, 0
  1343.     mov    al, 1            ; AL is number of lines to scroll.
  1344.     mov    ah, 6            ; BIOS: scroll up
  1345. ;    mov    cl, 0            ; upper-left-x of data to scroll
  1346. ;    mov    ch, 0            ; upper-left-y of data to scroll
  1347.     xor    cx, cx
  1348.     mov    dl, cs:max_x        ; lower-rite-x
  1349.     mov    dh, cs:max_y        ; lower-rite-y (zero based)
  1350.     int    10h            ; call BIOS to scroll a rectangle.
  1351.  
  1352. scrret:
  1353.     pop_all
  1354.     ret
  1355.  
  1356. scrOurself:    ; try scrolling screen ourself!
  1357.     push    es
  1358.     push    ds
  1359.  
  1360.     mov    dx, 3ceh        ; set write mode 1
  1361.     mov    ax, 105h
  1362.     out    dx, ax
  1363.  
  1364.     mov    ax, 40h            ; address abs40 segment
  1365.     mov    ds, ax
  1366.     assume    ds:ABS40
  1367.     mov    ax, crt_cols        ; calculate length of line in bytes
  1368.     mul    byte ptr ega_points
  1369.     mov    si, ax            ; source of move
  1370.     mov    cx, crt_len
  1371.     sub    cx, ax            ; number of bytes to move
  1372.  
  1373.     mov    bx, 0a000h        ; address display
  1374.     mov    ds, bx
  1375.     mov    es, bx
  1376.  
  1377.     xor    di, di            ; destination of move
  1378.     rep movsb            ; scroll
  1379.     
  1380.     mov    cx, ax            ; bytes in line = bytes to clear
  1381.  
  1382.     mov    ax, 05h            ; return to write mode 0
  1383.     out    dx, ax
  1384.  
  1385.     xor    ax, ax
  1386.     rep stosb            ; clear the line
  1387.  
  1388.     pop    ds            ; restore registers and return
  1389.     pop    es
  1390.     jmp    scrret
  1391.  
  1392. scroll_up    endp
  1393.  
  1394. if fast
  1395. ;-----move_back --------------------------------------------
  1396. ; This routine moves the display to offset zero.
  1397. ; all registers destroyed
  1398. ; alters:
  1399. ; cs:temp_val = original crt_start value
  1400. ; crt_start = 0
  1401. ; controller reset properly
  1402. move_back proc near
  1403.     mov    ax, ABS40
  1404.     mov    ds, ax
  1405.  
  1406.     assume    ds:ABS40
  1407.     mov    al, ega_rows
  1408.     inc    al
  1409.     mul    byte ptr crt_cols    ; words to move
  1410.     mov    cx, ax
  1411.     mov    si, crt_start
  1412.     mov    cs:temp_val, si        ; save this value
  1413.     xor    di, di
  1414.     mov    crt_start, di
  1415.     mov    bx, cursor_posn        ; y in bh, x in bl
  1416.     mov    al, byte ptr crt_cols
  1417.     mul    bh
  1418.     add    al, bl
  1419.     adc    ah, 0
  1420.     xchg    bx, ax            ; save cursor position in bx
  1421.     
  1422.     mov    ax, 0B800h
  1423.     mov    es, ax
  1424.     mov    ds, ax
  1425.  
  1426.     mov    dx, cx
  1427.     add    dx, cx            ; see if overlapping
  1428.     cmp    dx, si
  1429.     ja    slow_move
  1430. join_move:
  1431.     cld
  1432.     rep movsw            ; move data
  1433.     
  1434.     mov    dx, port_6845
  1435.     mov    al, 0ch            ; reset offset
  1436.     xor    ah,ah
  1437.     out    dx, ax
  1438.     inc    al
  1439.     out    dx, ax
  1440.     inc    al
  1441.     mov    ah, bh
  1442.     out    dx, ax
  1443.     inc    al
  1444.     mov    ah, bl
  1445.     out    dx, ax
  1446.     sti
  1447.     assume    ds:nothing
  1448.     ret
  1449.  
  1450. slow_move:    ; we gotta move to another spot first
  1451.     push    cx        ; save length
  1452.     dec    dx        ; length-2
  1453.     dec    dx
  1454.     add    si, dx        ; point to end
  1455.     mov    di, 7FFEh    ; safe location -- as safe as we can get
  1456.     std
  1457.     rep movsw        ; move from far end in case of overlap
  1458.                 ; (may happen on large displays)
  1459.     mov    dx, port_6845
  1460.     mov    si, di        ; source becomes destination
  1461.     inc    si        ; take care of last decrement
  1462.     inc    si
  1463.     mov    cx, si
  1464.     shr    cx, 1        ; word offset to start of new area
  1465.     mov    al, 0Ch        ; display at this new location
  1466.     mov    ah, ch
  1467.     out    dx, ax
  1468.     inc    al
  1469.     mov    ah, cl
  1470.     out    dx, ax
  1471.     pop    cx        ; reset all registers
  1472.     xor    di, di        ; destination is zero
  1473.     jmp    join_move    ; NOW move to destination
  1474.     
  1475. move_back    endp
  1476. endif    
  1477.  
  1478. if key_redef
  1479. ;---- lookup -----------------------------------------------
  1480. ; Called by getchar, peekchar, and key to see if a given key has
  1481. ; been redefined.
  1482. ; Sets AH to zero if AL is not zero (i.e. if AX is not a function key).
  1483. ; Returns with Z cleared if no redefinition; otherwise,
  1484. ; Z is set, SI points to redefinition string, CX is its length.
  1485. ; Preseves AL, all but CX and SI.
  1486. ; Redefinition table organization:
  1487. ;  Strings are stored in reversed order, first char last.
  1488. ;  The word following the string is the character to be replaced;
  1489. ;  the next word is the length of the string sans header.
  1490. ; param_end points to the last byte used by the parameter buffer;
  1491. ; redef_end points to the last word used by the redef table.
  1492.  
  1493. lookup    proc    near
  1494.     mov    si, redef_end        ; Start at end of table, move down.
  1495.     or    al, al
  1496.     jz    lu_lp
  1497.     mov    ah, 0            ; clear extraneous scan code
  1498. lu_lp:    cmp    si, param_end
  1499.     jbe    lu_notfound        ; If below redef table, exit.
  1500.     mov    cx, [si]
  1501.     cmp    ax, [si-2]        ; are you my mommy?
  1502.     jz    lu_gotit
  1503.     sub    si, 4
  1504.     sub    si, cx            ; point to next header
  1505.     jmp    lu_lp
  1506. lu_notfound:
  1507.     or    si, si            ; clear Z
  1508.     jmp    short lu_exit
  1509. lu_gotit:
  1510.     sub    si, 2
  1511.     sub    si, cx            ; point to lowest char in memory
  1512.     cmp    al, al            ; set Z
  1513. lu_exit:
  1514.     ret
  1515. lookup    endp
  1516. endif
  1517.  
  1518. ;---- searchbuf --------------------------------------------
  1519. ; Called by getchar and peekchar to see if any characters are
  1520. ; waiting to be gotten from sources other than BIOS.
  1521. ; Returns with Z set if no chars found, BX=keybuf & SI=keybuf.len otherwise.
  1522. searchbuf    proc    near
  1523.     ; Search the stuffahead buffers.
  1524. if key_redef
  1525.     mov    cx, 4            ; number of buffers to check for chars
  1526. else
  1527.     mov    cx, 3
  1528. endif
  1529.     mov    bx, offset fnkey - 4
  1530. sbloop: add    bx, 4            ; point to next buffer record
  1531.     mov    si, [bx].len
  1532.     or    si, si            ; empty?
  1533.     loopz    sbloop            ; if so, loop.
  1534.     ret
  1535. searchbuf    endp
  1536.  
  1537. ;---- getchar -----------------------------------------------
  1538. ; Returns AL = next char.
  1539. ; Trashes AX, BX, CX, BP, SI.
  1540. getchar proc    near
  1541. gc_searchbuf:
  1542.     ; See if any chars are waiting in stuffahead buffers.
  1543.     call    searchbuf
  1544.     jz    gc_trykbd        ; No chars?  Try the keyboard.
  1545.     ; A nonempty buffer was found.
  1546.     dec    [bx].len
  1547.     dec    si
  1548.     mov    bp, [bx].adr        ; get pointer to string
  1549.     mov    al, byte ptr ds:[bp][si]; get the char
  1550.     ; Recognize function key sequences, move them to highest priority
  1551.     ; queue.
  1552.     sub    si, 1            ; set carry if si=0
  1553.     jc    gc_nofnkey        ; no chars left -> nothing to protect.
  1554.     cmp    bx, offset fnkey
  1555.     jz    gc_nofnkey        ; already highest priority -> done.
  1556.     or    al, al
  1557.     jnz    gc_nofnkey        ; nonzero first byte -> not fnkey.
  1558.     ; Found a function key; move it to highest priority queue.
  1559.     dec    [bx].len
  1560.     mov    ah, byte ptr ds:[bp][si]; gec      [bx].len
  1561.     mov    ah, byte ptr ds:[bp][si]; get the second byte of fn key code
  1562. gc_fnkey:
  1563.     mov    fnkey.len, 1
  1564.     mov    fnkeybuf, ah        ; save it.
  1565. gc_nofnkey:
  1566.     ; Valid char in AL.  Return with it.
  1567.     jmp    short gcdone
  1568.  
  1569. gc_trykbd:
  1570.     ; Actually get a character from the keyboard.
  1571.     mov    ah, 0
  1572.     int    16h            ; BIOS returns with char in AX
  1573.     ; If it's Ctrl-break, it has already been taken care of.
  1574.     or    ax, ax
  1575.     jz    gc_trykbd
  1576.  
  1577. if key_redef
  1578.     ; Look in the reassignment table to see if it needs translation.
  1579.     call    lookup            ; Z=found; CX=length; SI=ptr
  1580.     jnz    gc_noredef
  1581.     ; Okay; set up the reassignment, and run thru the translation code.
  1582.     mov    xlatseq.len, cx
  1583.     mov    xlatseq.adr, si
  1584.     jmp    gc_searchbuf
  1585. endif
  1586. gc_noredef:
  1587.     ; Is it a function key?
  1588.     cmp    al, 0
  1589.     jz    gc_fnkey        ; yep- special treatment.
  1590. gcdone: ret    ; with character in AL.
  1591.  
  1592. getchar endp
  1593.  
  1594. ;---- peekchar -----------------------------------------------
  1595. ; Returns Z if no character ready, AL=char otherwise.
  1596. ; Trashes AX, BX, CX, BP, SI.
  1597. peekchar    proc    near
  1598. pc_searchbuf:
  1599.     call    searchbuf
  1600.     jz    pc_trykbd        ; No chars?  Try the keyboard.
  1601.     ; A nonempty buffer was found.
  1602.     dec    si
  1603.     mov    bp, [bx].adr        ; get pointer to string
  1604.     mov    al, byte ptr ds:[bp][si]; get the char
  1605.     ; Valid char from buffer in AL.     Return with it.
  1606.     jmp    short pcdone
  1607. pc_trykbd:
  1608.     ; Actually peek at the keyboard.
  1609.     mov    ah, 1
  1610.     int    16h            ; BIOS returns with char in AX
  1611.     jz    pcexit
  1612.     ; If it's control-break, it's already been taken care of.
  1613.     or    ax, ax
  1614.     jnz    pc_notbrk
  1615.     mov    ah, 0
  1616.     int    16h            ; so get rid of it!
  1617.     jmp    short pc_trykbd
  1618. pc_notbrk:
  1619. if key_redef
  1620.     ; Look in the reassignment table to see if it needs translation.
  1621.     call    lookup            ; Z=found; CX=length; SI=ptr
  1622.     jnz    pcdone            ; Nope; just return the char.
  1623.     ; Okay; get the first code to be returned.
  1624.     add    si, cx
  1625.     mov    al, [si-1]
  1626. endif
  1627. pcdone: or    ah, 1            ; NZ; char ready!
  1628. pcexit: ret    ; with character in AL, Z true if no char waiting.
  1629. peekchar    endp
  1630.  
  1631. ;----- set_gmode ------------------------------------------------
  1632. ; Set gmode_flag based on mode byte in register al
  1633. set_gmode proc  near
  1634.     gmode_code            ; a macro in nnansi_d.asm
  1635.     ret
  1636. set_gmode    endp
  1637.  
  1638.  
  1639. ;---- beep ------------------------------------------------------
  1640. ; Beep speaker; period given by beep_div, duration by beep_len.
  1641. ; Preserves all registers.
  1642.  
  1643. beep_div    equ    1300        ; fairly close to IBM beep
  1644. beep_len    equ    3        ; 3/18 sec- shorter than IBM
  1645.  
  1646. beep    proc    near
  1647.     push_all
  1648.  
  1649.     mov    al, 10110110b        ; select 8253
  1650.     mov    dx, 43h            ; control port address
  1651.     out    dx, al
  1652.     dec    dx            ; timer 2 address
  1653.     mov    ax, beep_div
  1654.     jmp    $+2
  1655.     out    dx, al            ; low byte of divisor
  1656.     xchg    ah, al
  1657.     jmp    $+2
  1658.     out    dx, al            ; high byte of divisor
  1659.     mov    dx, 61h
  1660.     jmp    $+2
  1661.     in    al, dx            ; get current value of control bits
  1662.     push    ax
  1663.     or    al, 3
  1664.     jmp    $+2
  1665.     out    dx, al            ; turn speaker on
  1666.  
  1667.     ; Wait for desired duration by monitoring time-of-day 18 Hz clock
  1668.     push    es
  1669.     mov    ax, ABS40
  1670.     mov    es, ax
  1671.     assume    es:ABS40
  1672.     mov    bx, timer_low
  1673.     mov    cx, -1
  1674. beeplp: mov    ax, timer_low
  1675.     sub    ax, bx
  1676.     cmp    ax,     beep_len
  1677.     jg    beepover
  1678.     loop    beeplp
  1679. beepover:
  1680.     pop    es
  1681.     assume    es:CODE
  1682.  
  1683.     ; Turn off speaker
  1684.     pop    ax
  1685.     and    al, not 3        ; turn speaker off
  1686.     out    dx, al
  1687.     pop_all
  1688.     ret
  1689. beep    endp
  1690. CODE    ends
  1691.  
  1692.     end                ; of nansi.asm
  1693.  
  1694.